﻿using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using RainEngine.Rendering;

namespace RainEngine.Effects
{
    /// <summary>
    /// 对绘制公告牌的Bilboard.fx的封装。
    /// </summary>
    public class BillBoardEffect : Effect 
    {
        EffectParameter worldParam;
        EffectParameter viewProjParam;
        EffectParameter eyePositionParam;              
        
        EffectParameter textureParam;
        EffectParameter upDirParam;
        EffectParameter alphaTestParam;
        EffectParameter alphaTestGreaterParam;
        EffectParameter alphaTestValueParam;
        EffectParameter clipPlaneEnabledParam;
        EffectParameter clipPlaneParam;

        Matrix world = Matrix.Identity;
        Matrix view = Matrix.Identity;
        Matrix projection = Matrix.Identity;
        bool clipPlaneEnabled = false;

        protected internal EffectDirtyFlags dirtyFlags = EffectDirtyFlags.All;

        /// <summary>
        /// 获取或设置世界矩阵。世界矩阵改变时需要重新设置effect的WorldViewProj。
        /// </summary>
        public Matrix World
        {
            get { return world; }
            set
            {
                world = value;
                dirtyFlags |= EffectDirtyFlags.WorldViewProj;
            }
        }


        /// <summary>
        /// 获取或设置，视矩阵视矩阵改变时需要重新设置effect的WorldViewProj。
        /// </summary>
        public Matrix View
        {
            get { return view; }
            set
            {
                view = value;
                dirtyFlags |= EffectDirtyFlags.WorldViewProj;
            }
        }

        /// <summary>
        /// 获取或设置投影矩阵，投影矩阵改变时需要重新设置effect的WorldViewProj。
        /// </summary>
        public virtual Matrix Projection
        {
            get { return projection; }
            set
            {
                projection = value;
                dirtyFlags |= EffectDirtyFlags.WorldViewProj;
            }
        }

        /// <summary>
        /// 获取或设置相机位置
        /// </summary>
        public Vector3 EyePosition
        {
            get { return eyePositionParam.GetValueVector3(); }
            set { eyePositionParam.SetValue(value); }
        }

        /// <summary>
        /// 获取或设置剪裁平面，将这个值设置为Vector.Zero就关闭剪裁
        /// </summary>
        public Vector4 ClipPlane
        {
            get { return clipPlaneParam.GetValueVector4(); }
            set
            {
                if (value == Vector4.Zero)
                    clipPlaneEnabled = false;
                else
                    clipPlaneEnabled = true;
                clipPlaneEnabledParam.SetValue(clipPlaneEnabled);
                clipPlaneParam.SetValue(value);
            }
        }
        
        /// <summary>
        /// 获取或设置纹理
        /// </summary>
        public Texture2D Texture
        {
            get { return textureParam.GetValueTexture2D(); }
            set { textureParam.SetValue(value); }
        }

        /// <summary>
        /// 获取或设置旋转轴方向，默认为向上方向
        /// </summary>
        public Vector3 Up
        {
            get { return upDirParam.GetValueVector3(); }
            set { upDirParam.SetValue(value); }
        }

        /// <summary>
        /// 获取或设置是否进行AlphaTest
        /// </summary>
        public bool AlphaTest
        {
            get { return alphaTestParam.GetValueBoolean(); }
            set { alphaTestParam .SetValue (value) ;}
        }

        /// <summary>
        /// 获取或设置是否开启AlphaTestGreater的混合，默认为true
        /// </summary>
        public bool AlphaTestGreater
        {
            get { return alphaTestGreaterParam.GetValueBoolean(); }
            set { alphaTestGreaterParam.SetValue(value); }
        }

        /// <summary>
        /// 获取或设置Alpha的阈值
        /// </summary>
        public float AlphaTestValue
        {
            get { return alphaTestValueParam.GetValueSingle(); }
            set { alphaTestValueParam.SetValue(value); }
        }

        /// <summary>
        /// 创建一个新BillBoardEffect对象。
        /// </summary>
        public BillBoardEffect(GraphicsDevice device)
            : base(device, Resource.BillboardEffectBin )
        {
            CacheEffectParameters(null); 
        }

        /// <summary>
        /// 通过从一个已经存在的对象中复制参数创建一个新BillBoardEffect对象。
        /// </summary>
        protected BillBoardEffect(BillBoardEffect cloneSource)
            : base(cloneSource)
        {
            CacheEffectParameters(cloneSource);

            world = cloneSource.world;
            view = cloneSource.view;
            projection = cloneSource.projection;
            clipPlaneEnabled = cloneSource.clipPlaneEnabled;
        }


        /// <summary>
        /// 创建一个当前BillBoardEffectt对象的副本。
        /// </summary>
        public override Effect Clone()
        {
            return new BillBoardEffect(this);
        }

        /// <summary>
        /// 缓存effect的参数。
        /// </summary>
        void CacheEffectParameters(BillBoardEffect cloneSource)
        {
            worldParam = Parameters["World"];
            viewProjParam = Parameters["ViewProj"];
            eyePositionParam = Parameters["EyePosition"];
            textureParam = Parameters["Texture"];
            upDirParam = Parameters["Up"];
            alphaTestParam = Parameters["AlphaTest"];
            alphaTestGreaterParam = Parameters["AlphaTestGreater"];
            alphaTestValueParam =Parameters ["AlphaTestValue"];

            clipPlaneEnabledParam = Parameters["ClipPlaneEnabled"];
            clipPlaneParam = Parameters["ClipPlane"];
        }


        /// <summary>
        /// 在施加effect前根据需要重新计算effect参数。
        /// </summary>
        protected override void OnApply()
        {
            if ((dirtyFlags & EffectDirtyFlags.WorldViewProj) != 0)
            {
                Matrix viewProj = Matrix.Multiply(view, projection);
                viewProjParam.SetValue(viewProj);
                worldParam.SetValue(world);

                dirtyFlags &= ~EffectDirtyFlags.WorldViewProj;
            }                
        }
    }
}